<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8">
	<title>25字母25单词挑战</title>
	<style>
		html,
		body {
			background-color: #000;
			color: #FFF;
			font-family: Bahnschrift, Consolas, 'Courier New', Courier, monospace;
			font-weight: 100;
			user-select: none;
		}

		h1,
		h2,
		h3,
		h4,
		h5,
		h6,
		p {
			margin: 5px;
		}

		a {
			color: #FFF;
			text-decoration: underline;
		}

		td {
			text-align: center;
		}

		.option {
			margin: 5px;
			background-color: #444;
			width: 50px;
			height: 50px;
			border-radius: 5px;

			display: flex;
			justify-content: center;
			align-items: center;

			font-size: 36px;
			font-weight: 900;

			cursor: pointer;
		}

		.chosen-option {
			background-color: #CCC;
			color: #000;
		}
	</style>
</head>

<body style="display: flex; flex-direction: column; align-items: center">
	<h1>25字母25单词挑战</h1>
	<p>规则：你需要在26个英文字母中选出25个，填入5×5的矩阵中。</p>
	<p>你需要使得矩阵中横、竖、斜出现的，长度≥3的英语单词达到25个。</p>
	<p>注：本挑战中的英文单词仅限于<a href="https://imalyd.gitee.io/imalyd-lab/misc/TheOxford3000.pdf"><b>牛津3000词汇</b></a>。</p>
	<p>操作方法：依次点击两个字母可以交换它们的顺序。</p>
	<p>现在矩阵中有<span id="cnt" style="font-size: 72px; font-weight: 500;">0</span>个单词</p>
	<table>
		<tbody>
			<tr>
				<td>多余的字母</td>
				<td style="min-width: 120px;">5×5矩阵</td>
				<td>出现的单词</td>
			</tr>
			<tr>
				<td>
					<div style="display: flex;">
						<div class="option" id="m25">Z</div>
					</div>
				</td>
				<td>
					<div style="display: flex; flex-direction: column;">
						<div style="display: flex;">
							<div class="option" id="m0">A</div>
							<div class="option" id="m1">B</div>
							<div class="option" id="m2">C</div>
							<div class="option" id="m3">D</div>
							<div class="option" id="m4">E</div>
						</div>
						<div style="display: flex;">
							<div class="option" id="m5">F</div>
							<div class="option" id="m6">G</div>
							<div class="option" id="m7">H</div>
							<div class="option" id="m8">I</div>
							<div class="option" id="m9">J</div>
						</div>
						<div style="display: flex;">
							<div class="option" id="m10">K</div>
							<div class="option" id="m11">L</div>
							<div class="option" id="m12">M</div>
							<div class="option" id="m13">N</div>
							<div class="option" id="m14">O</div>
						</div>
						<div style="display: flex;">
							<div class="option" id="m15">P</div>
							<div class="option" id="m16">Q</div>
							<div class="option" id="m17">R</div>
							<div class="option" id="m18">S</div>
							<div class="option" id="m19">T</div>
						</div>
						<div style="display: flex;">
							<div class="option" id="m20">U</div>
							<div class="option" id="m21">V</div>
							<div class="option" id="m22">W</div>
							<div class="option" id="m23">X</div>
							<div class="option" id="m24">Y</div>
						</div>
					</div>
				</td>
				<td id="words" style="display: flex; flex-wrap: wrap; max-width: 200px;"></td>
			</tr>
		</tbody>
	</table>
</body>
<script>
	const wordList = ["able", "about", "above", "abuse", "acid", "act", "actor", "admit", "adopt", "adult", "after", "age", "aged", "agent", "ago", "aid", "aim", "air", "alive", "alone", "along", "aloud", "also", "alter", "among", "amuse", "and", "anger", "angle", "angry", "ankle", "any", "april", "argue", "arise", "arm", "armed", "arms", "army", "art", "aside", "ask", "atom", "aunt", "avoid", "awful", "back", "bad", "badly", "bag", "bake", "ban", "band", "bank", "bar", "base", "based", "basic", "bath", "bay", "beach", "beak", "bear", "beard", "beat", "bed", "begin", "below", "belt", "bend", "bent", "best", "bet", "bid", "big", "bike", "bin", "bird", "birth", "bit", "bite", "black", "blade", "blame", "blank", "blind", "block", "blow", "blue", "board", "boat", "body", "boil", "bone", "bore", "bored", "born", "both", "bound", "bowl", "box", "boy", "brain", "brand", "brave", "bread", "break", "brick", "brief", "bring", "broad", "brown", "brush", "build", "bunch", "burn", "burnt", "burst", "bury", "bus", "bush", "busy", "but", "buy", "buyer", "bye", "cable", "cake", "calm", "camp", "can", "candy", "cap", "car", "card", "care", "case", "cash", "cast", "cat", "cause", "cent", "chain", "chair", "chart", "chase", "chat", "cheap", "cheat", "chest", "chew", "chief", "child", "chin", "chip", "chop", "city", "claim", "clap", "clean", "clear", "clerk", "climb", "close", "cloth", "cloud", "club", "coal", "coast", "coat", "code", "coin", "cold", "come", "cope", "copy", "core", "cost", "cough", "could", "count", "court", "cover", "cow", "craft", "crash", "crazy", "cream", "crime", "crisp", "crop", "crowd", "crown", "cruel", "crush", "cry", "cup", "curb", "cure", "curl", "curly", "curve", "cut", "daily", "damp", "dance", "dare", "dark", "date", "day", "deaf", "deal", "dear", "death", "debt", "decay", "delay", "deny", "depth", "desk", "diary", "die", "diet", "dig", "dirt", "dirty", "disc", "dish", "disk", "dog", "dot", "doubt", "down", "dozen", "draft", "drag", "draw", "dream", "drink", "drive", "drop", "drug", "drum", "drunk", "dry", "due", "dump", "dust", "duty", "dying", "each", "ear", "early", "earn", "earth", "east", "easy", "eat", "eight", "elbow", "email", "empty", "end", "enjoy", "entry", "equal", "etc", "euro", "evil", "exact", "exam", "exist", "exit", "extra", "face", "fact", "fail", "faint", "fair", "faith", "false", "fame", "fan", "fancy", "far", "farm", "fast", "fat", "fault", "fear", "fetch", "few", "field", "fight", "file", "film", "final", "find", "fine", "fire", "firm", "first", "fish", "fit", "five", "fix", "fixed", "flag", "flame", "flash", "flat", "flesh", "float", "flour", "flow", "flu", "fly", "focus", "fold", "for", "force", "fork", "form", "forty", "found", "four", "frame", "fresh", "from", "front", "fruit", "fry", "fuel", "fun", "fund", "fur", "gain", "game", "gap", "gas", "gate", "gear", "get", "giant", "gift", "girl", "give", "glad", "glove", "glue", "goal", "god", "gold", "grab", "grade", "grain", "gram", "grand", "grant", "grave", "great", "grey", "group", "grow", "guard", "guest", "guide", "gun", "guy", "habit", "hair", "half", "hand", "hang", "hard", "harm", "hat", "hate", "have", "head", "heal", "hear", "heart", "heat", "heavy", "help", "her", "hero", "hers", "hide", "him", "hip", "hire", "his", "hit", "hold", "hole", "holy", "home", "hope", "horn", "horse", "host", "hot", "hotel", "hour", "house", "how", "huge", "human", "hunt", "hurt", "ice", "idea", "ideal", "image", "imply", "inch", "index", "ink", "into", "iron", "item", "its", "jam", "jeans", "job", "join", "joint", "joke", "joy", "judge", "juice", "july", "jump", "june", "just", "key", "kid", "kind", "king", "knife", "knit", "knot", "know", "lab", "lack", "lady", "lake", "lamp", "land", "lane", "large", "last", "late", "later", "laugh", "law", "lay", "layer", "lazy", "lead", "leaf", "lean", "learn", "least", "left", "leg", "lemon", "lend", "let", "lid", "lie", "life", "lift", "light", "like", "line", "link", "lip", "list", "litre", "live", "load", "loan", "lock", "logic", "long", "lord", "lose", "lost", "lot", "loud", "love", "lover", "low", "luck", "lucky", "lump", "lunch", "lung", "mad", "magic", "mail", "main", "major", "make", "male", "man", "many", "map", "march", "mark", "match", "mate", "may", "maybe", "mayor", "meal", "mean", "means", "meat", "media", "melt", "menu", "metal", "mid", "might", "mild", "mile", "milk", "mind", "mine", "minor", "mix", "mixed", "model", "money", "month", "moral", "more", "most", "mount", "mouse", "mouth", "move", "movie", "mrs", "much", "mud", "music", "must", "nail", "naked", "name", "navy", "near", "neat", "neck", "nest", "net", "new", "newly", "news", "next", "nice", "night", "noise", "noisy", "nor", "north", "nose", "not", "note", "novel", "now", "nurse", "nut", "obey", "ocean", "often", "oil", "old", "once", "one", "only", "open", "organ", "other", "ought", "our", "ours", "out", "outer", "oven", "over", "owe", "own", "owner", "pace", "pack", "page", "pain", "paint", "pair", "pale", "pan", "panel", "pants", "park", "part", "party", "past", "path", "pause", "pay", "peak", "pen", "per", "pet", "phase", "phone", "piano", "pick", "pig", "pile", "pilot", "pin", "pink", "pint", "pitch", "pity", "place", "plain", "plan", "plane", "plant", "plate", "play", "plot", "plug", "plus", "poem", "point", "pole", "port", "pose", "post", "pot", "pound", "pour", "power", "pray", "price", "pride", "print", "prize", "proud", "prove", "pub", "punch", "pure", "push", "put", "quick", "quiet", "quit", "quite", "quote", "race", "radio", "rail", "rain", "raise", "range", "rank", "rapid", "rate", "raw", "reach", "react", "read", "ready", "real", "red", "relax", "rely", "rent", "reply", "rest", "rice", "rich", "rid", "ride", "right", "ring", "rise", "risk", "rival", "road", "rob", "rock", "role", "rope", "rough", "round", "route", "row", "royal", "rub", "rude", "ruin", "rule", "run", "rush", "sack", "sad", "sadly", "safe", "sail", "sale", "salt", "salty", "same", "sand", "sauce", "save", "say", "scale", "scare", "score", "screw", "sea", "seal", "seat", "self", "send", "set", "sew", "sex", "shade", "shake", "shame", "shape", "share", "sharp", "shave", "she", "shelf", "shift", "shine", "shiny", "ship", "shirt", "shock", "shoe", "shop", "short", "shot", "shout", "show", "shut", "shy", "sick", "side", "sight", "sign", "silk", "since", "sing", "sink", "sir", "sit", "site", "six", "sixth", "sixty", "size", "skin", "skirt", "sky", "slice", "slide", "slip", "slope", "slow", "smart", "smile", "smoke", "snake", "snow", "soap", "sock", "soft", "soil", "solid", "solve", "some", "son", "song", "sore", "sort", "soul", "sound", "soup", "sour", "south", "space", "spare", "speak", "spend", "spice", "spicy", "spin", "spite", "split", "spoil", "sport", "spot", "spray", "stage", "stair", "stamp", "stand", "star", "stare", "stay", "steal", "steam", "step", "stick", "sting", "stir", "stock", "stone", "stop", "store", "storm", "story", "stove", "strip", "study", "style", "such", "suck", "sugar", "suit", "sum", "sun", "sure", "swear", "sweat", "swim", "swing", "table", "tail", "take", "talk", "tank", "tap", "tape", "task", "tax", "taxi", "tea", "teach", "team", "tear", "ten", "tend", "term", "than", "thank", "the", "their", "them", "then", "they", "thick", "thief", "thin", "thing", "think", "third", "this", "throw", "thumb", "thus", "tidy", "tie", "time", "tin", "tiny", "tip", "tire", "tired", "today", "toe", "ton", "tone", "top", "topic", "touch", "tough", "tour", "towel", "tower", "town", "toy", "trace", "track", "trade", "train", "trap", "trend", "trial", "trick", "trip", "truck", "true", "truly", "try", "tube", "tune", "turn", "twice", "twin", "two", "type", "tyre", "ugly", "uncle", "under", "undo", "unit", "unite", "until", "upon", "upset", "urban", "urge", "use", "used", "user", "valid", "value", "van", "vary", "vast", "very", "via", "video", "view", "virus", "vital", "voice", "vote", "wage", "waist", "wait", "wake", "walk", "want", "war", "warm", "warn", "wash", "waste", "watch", "water", "wave", "way", "weak", "wear", "web", "weigh", "west", "wet", "what", "when", "while", "white", "who", "whole", "whom", "whose", "why", "wide", "width", "wife", "wild", "win", "wind", "wine", "wing", "wire", "wise", "wish", "with", "woman", "word", "work", "world", "worse", "worst", "worth", "would", "wound", "wrap", "wrist", "write", "wrong", "yard", "yawn", "yeah", "year", "yes", "yet", "you", "young", "your", "yours", "youth", "zero", "zone"]

	const matrixWidth = 5
	const matrixHeight = 5
	const alphabetSize = 26
	const unusedId = 25
	const goal = 25
	const diffX = [1, 1, 0, -1, -1, -1, 0, 1]
	const diffY = [0, 1, 1, 1, 0, -1, -1, -1]

	const countElement = document.getElementById('cnt')
	const wordsElement = document.getElementById('words')

	let matrixElements = new Array(26)
	for (let i = 0; i < alphabetSize; ++i)
		matrixElements[i] = document.getElementById('m' + i)

	let matrix = new Array(26)
	for (let i = 0; i < alphabetSize; ++i)
		matrix[i] = String.fromCharCode(i + 97)

	let wordMap = {}
	wordList.forEach(function (word) { wordMap[word] = true })
	let appearedWords = []
	let firstWin = true

	function update() {
		appearedWords = []
		for (let d = 0; d < 8; ++d) {
			let dx = diffX[d], dy = diffY[d]
			for (let i = 0; i < matrixWidth; ++i) {
				for (let j = 0; j < matrixHeight; ++j) {
					let s = ''
					for (let x = i, y = j; 0 <= x && x < matrixWidth && 0 <= y && y < matrixHeight; x += dx, y += dy) {
						s += matrix[x * matrixHeight + y]
						if (s in wordMap)
							appearedWords.push(s)
					}
				}
			}
		}
		appearedWords.sort()
		if (appearedWords.length >= goal && firstWin) {
			firstWin = false
			alert('恭喜你，你赢了！')
		}
	}

	let lastChoice = undefined

	function render() {
		for (let i = 0; i < alphabetSize; ++i) {
			matrixElements[i].innerHTML = matrix[i].toUpperCase()
			if (lastChoice === i)
				matrixElements[i].classList.add('chosen-option')
			else
				matrixElements[i].classList.remove('chosen-option')
		}

		countElement.innerHTML = appearedWords.length.toFixed(0)

		wordsElement.innerHTML = ''
		appearedWords.forEach(function (word) {
			let wordElement = document.createElement('p')
			wordElement.innerHTML = word
			wordsElement.appendChild(wordElement)
		})
	}
	update()
	render()

	function click(choice) {
		if (lastChoice === undefined)
			lastChoice = choice
		else {
			let t = matrix[lastChoice]
			matrix[lastChoice] = matrix[choice]
			matrix[choice] = t
			lastChoice = undefined

			update()
		}
		render()
	}
	for (let i = 0; i < alphabetSize; ++i)
		matrixElements[i].addEventListener('click', function () { click(i) })
</script>

</html>